/***************************************************************************
 *   Copyright (C) 2010 by Oleg Goncharov  *
 *   $EMAIL$                           *                          
 *                                                                         *
 *   This file is part of ChessVision.                                     *
 *                                                                         *
 *   ChessVision is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include "error/cerror.h"
#include "cgnuchessenvelop.h"

using namespace std;

namespace Chess {

CGnuchessEnvelop::CGnuchessEnvelop(const char * command) {
	int in[2], out[2];
	int pid;
	if (pipe(in)) throw Errors::CStdError();
	if (pipe(out)) throw Errors::CStdError();
	
	pid = fork();
	if (pid == -1) throw Errors::CStdError();
	if ( pid == 0 ) {
		if (close(in[0])) throw Errors::CStdError();
		if (close(out[1])) throw Errors::CStdError();
		if (-1 == dup2(in[1],1)) throw Errors::CStdError();
		if (-1 == dup2(out[0],0)) throw Errors::CStdError();
		
		char * argv[3];
		argv[0] = new char[strlen(command) + 1];
		strcpy(argv[0], command);
		argv[1] = new char[9];
		strcpy(argv[1], "--xboard");
		argv[2] = 0;
		
		execvp(command, argv);
		
		throw Errors::CStdError();
	}
	
	if (close(in[1])) throw Errors::CStdError();
	if (close(out[0])) throw Errors::CStdError();
	
	//bin = new __gnu_cxx::stdio_filebuf<char>(hin = in[0], ios_base::in);
	//bout = new __gnu_cxx::stdio_filebuf<char>(hout = out[1], ios_base::out);
	((ios*) &this->in)->rdbuf(new __gnu_cxx::stdio_filebuf<char>(hin = in[0], ios_base::in)); 
	this->in.setf(ios::unitbuf);
	((ios*) &this->out)->rdbuf(new __gnu_cxx::stdio_filebuf<char>(hout = out[1], ios_base::out));
	this->out.setf(ios::unitbuf);
}

void CGnuchessEnvelop:: gl() { 
	std::getline(in, line); 
	//std::cout << line << std::endl;
	if (!in.good()) throw Errors::CError("gl(): Communication problem.");  
}	

bool CGnuchessEnvelop::CheckMove(const SMove& mv) {
	out << "force" << endl;
	out << (string) mv << endl;
	out << "z" << endl;
	do { 
		gl(); 
	} while (0 != line.compare(0, 12, "Illegal move"));
	if (line[14] == 'z') {
		out << "undo" << endl;	
		return true;	
	}
	gl();
	return false;
}

bool CGnuchessEnvelop::Move(const SMove& mv) {	
	out << "force" << endl;
	out << (string) mv << endl;
	out << "z" << endl;
	do { 
		gl(); 
	} while (0 != line.compare(0, 12, "Illegal move"));
	if (line[14] != 'z') { gl(); return false; }
	return true;
}

void CGnuchessEnvelop::Go(SMove& mv) {	
	out << "go" << endl;
	do {
		gl();
	} while (0 != line.compare(0, 5, "move "));
	mv.FromString(line.substr(5,4));
}

void CGnuchessEnvelop::Start() {
	out << "new" << endl;
}

void CGnuchessEnvelop::Command(const char *cmd) {
	out << cmd << endl;
}

CGnuchessEnvelop::~CGnuchessEnvelop() {
	out << "quit" << endl;
	if (close(hin) || close(hout)) throw Errors::CStdError();
}

}
